package com.microservices.yucloud.appweb.service;

import com.microservices.yucloud.appweb.contants.BizOrderConstants;
import com.microservices.yucloud.appweb.contants.BizOrderErrorCode;
import com.microservices.yucloud.appweb.domain.BizOrderResponse;
import com.microservices.yucloud.appweb.domain.Orders;
import com.microservices.yucloud.appweb.domain.StateMachineRequest;
import com.microservices.yucloud.appweb.enums.BizOrderEventsEnum;
import com.microservices.yucloud.appweb.enums.BizOrderStatusEnum;
import com.microservices.yucloud.appweb.exception.BusinessException;
import com.microservices.yucloud.appweb.utils.BizOrderStateMachineUtils;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.encoder.org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author zy
 * @date 2019-09-05 15:12
 **/
@Slf4j
@Service
public class OrderStateService {

    @Autowired
    @Qualifier("bizOrderStateMachine")
    private StateMachine<BizOrderStatusEnum, BizOrderEventsEnum> bizOrderStateMachine;

    @Autowired
    @Qualifier("bizOrderRedisStateMachinePersister")
    private StateMachinePersister<BizOrderStatusEnum, BizOrderEventsEnum, String> stateMachinePersister;


    /**
     * 发送状态机event，同时处理状态机持久化
     */
    @Transactional(rollbackFor = {BusinessException.class, Exception.class})
    public BizOrderResponse sendOrderStateEvent(StateMachineRequest<Orders> request,
                                                BizOrderEventsEnum eventEnum) throws Exception {

        // 获取状态机信息
        StateMachine<BizOrderStatusEnum, BizOrderEventsEnum> stateMachine = getStateMachine(request);

        boolean result = handleEvent(stateMachine, request, eventEnum);

        BizOrderResponse response = new BizOrderResponse();
        if (!result) {
            response.setCode(BizOrderErrorCode.ORDER_STATE_MACHINE_EXECUTE_ERR);
            response.setMsg("订单状态操作异常");
        }

        log.info("order status change response is {}", response);

        // 更新redis中数据
        // 发送event写log的动作还是放在业务里面，这里无法囊括所有业务数据
        if (result) {
            // 将数据持久化到redis中,以bizOrderId作为对应Key
            try {
                stateMachinePersister.persist(stateMachine, request.getBizCode());
            } catch (Exception e) {
                log.error("Persist bizOrderStateMachine error", e);
            }
            response.setBizOrderId(request.getBizCode());
            response.success();
        }
        return response;
    }

    /**
     * 状态处理的通用操作抽取
     *
     * @param stateMachine 状态机
     * @param request      状态变更请求
     * @return 执行结果
     * @throws Exception 异常
     */
    private boolean handleEvent(StateMachine<BizOrderStatusEnum, BizOrderEventsEnum> stateMachine, StateMachineRequest<Orders> request, BizOrderEventsEnum eventEnum) {

        log.info("statusChangeCommonOps statemachine send event={}", eventEnum);

        // 执行引擎，sendEvent,result为执行结果,通过actionListener跳转到对应的Action
        Message<BizOrderEventsEnum> eventMsg = MessageBuilder.withPayload(eventEnum).setHeader(BizOrderConstants.STATE_MACHINE_BIZ_ORDER, request).build();

        // 取到对应的状态机，判断是否可以执行
        boolean result;

        // 状态机的当前状态，只有在执行结束后才会变化，也就是节点对应的action执行完才会变更
        // 所以在result=true的情况下，更新状态机的持久化状态才有效
        if (BizOrderStateMachineUtils.acceptEvent(stateMachine, eventMsg)) {
            result = stateMachine.sendEvent(eventMsg);
            log.info("change order statemachine send event={},result={}", eventMsg, result);
        } else {
            throw new BusinessException(BizOrderErrorCode.NO_ORDER_STATE_MACHINE_TRANSACTION_ERR, "当前订单无法执行请求动作");
        }
        return result;
    }

    /**
     * 获取statemachine实例
     */
    private StateMachine<BizOrderStatusEnum, BizOrderEventsEnum> getStateMachine(StateMachineRequest<Orders> request) throws Exception {
        log.info("bizOrder request={}", request);

        if (!BizOrderStatusEnum.isInitialStatus(request.getBizModel().getState())) {
            if (!StringUtils.equals(request.getBizCode(), request.getBizModel().getId())) {
                throw new BusinessException(BizOrderErrorCode.ORDER_COMMON_ILLEGAL_ARGUMENT, "请求数据异常");
            }
            // 从redis中获取对应的statemachine，并判断当前节点是否可以满足，如果无法从redis中获取对应的的statemachine，则取自DB
            StateMachine<BizOrderStatusEnum, BizOrderEventsEnum> stateMachine = stateMachinePersister.restore(bizOrderStateMachine, request.getBizModel().getId());
            // 由于DB中已持久化，基本上不太可能出现null的情况，目前唯一能想到会出现的情况就是缓存击穿，先抛错
            if (null == stateMachine) {
                throw new BusinessException(BizOrderErrorCode.NO_CORRESPONDING_STATEMACHINE_ERR, "不存在订单对应的状态机实例");
            }
            log.info("order stateMachine is {}", stateMachine.getState().getId());
            return stateMachine;
        }
        log.info("order stateMachine is {}", bizOrderStateMachine.getState().getId());

        return bizOrderStateMachine;
    }

}
